package games.ticTacToe_;


public class SeaChess {
	private static final int DEFAULT_MIN_NUMBER_OF_ROWS = 3;
	private static final int DEFAULT_MAX_NUMBER_OF_ROWS = 10;
	private static final int DEFAULT_MIN_NUMBER_OF_COLUMNS = 3;
	private static final int DEFAULT_MAX_NUMBER_OF_COLUMNS = 10;
	private static final int DEFAULT_MAX_NUMBER_OF_SIMILAR_FIELDS_TO_WIN=5;
	private static final char FIRST_PLAYER_MARK = 'X';
	private static final char SECOND_PLAYER_MARK = 'O';
	private static final char EMPTY_SYMBOL = '.';
	private char[][] board;
	private int rows;
	private int columns;
	private int numberOfFieldsToWin;
	private boolean isFirstPlayer;
	private int currentCol;
	private int currentRow;

	public boolean isFirstPlayer() {
		return isFirstPlayer;
	}

	public SeaChess(int rows, int columns, int numberOfFieldsToWin) {
		setRows(rows);
		setColumns(columns);
		setNumberOfFieldsToWin(numberOfFieldsToWin);
		drawBoard();
		isFirstPlayer = true;
	}
	/*
	 * Initialize the empty sea chess board.
	 */
	private void drawBoard() {
		board = new char[rows][columns];
		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < columns; j++) {
				board[i][j] = EMPTY_SYMBOL;
			}
		}
	}
	/*
	 * Prints the board at the current stage of the game.
	 */
	public void printBoard() {
		System.out.print("  ");
		for (int i = 0; i < columns; i++) {
			System.out.print(i + " ");
		}
		System.out.println();
		for (int i = 0; i < rows; i++) {
			System.out.print(i + " ");
			for (int j = 0; j < columns; j++) {
				System.out.print(board[i][j] + " ");
			}

			System.out.println();
		}
	}
	/*
	 *  Marks the field chose by the player.
	 */
	public void mark(int row, int column) {
		if (!(row >= 0 && row < this.rows && column >= 0 && column < this.columns)) {
			System.out.println("Invalid values. Try again!");
			return;
		}
		if (board[row][column] == EMPTY_SYMBOL) {
			if (isFirstPlayer) {
				board[row][column] = FIRST_PLAYER_MARK;
				isFirstPlayer = false;
				printBoard();
			} else {
				board[row][column] = SECOND_PLAYER_MARK;
				isFirstPlayer = true;
				printBoard();
			}
			currentCol = column;
			currentRow = row;
		} else {
			System.out.println("Field is already marked. Mark again!");
		}
	}
	
	/*
	 *  Checks if there is a winner.
	 */
	public boolean hasWinner() {
		if (isEmptyBoard()) {
			return false;
		}
		if (checkDiagonal()) {
			isFirstPlayer = !isFirstPlayer;
			return true;
		}
		if (checkAntiDiagonal()) {
			isFirstPlayer = !isFirstPlayer;
			return true;
		}
		if (checkHorizontal()) {
			isFirstPlayer = !isFirstPlayer;
			return true;
		}
		if (checkVertical()) {
			isFirstPlayer = !isFirstPlayer;
			return true;
		}
		return false;
	}
	
	private boolean isEmptyBoard(){
		for (int i = 0; i < board.length; i++) {
			for (int j = 0; j < board[i].length; j++) {
				if (board[i][j]!=EMPTY_SYMBOL) {
					return false;
				}
			}
		}

		return true;
	}
	public boolean isDraw(){
		for (int i = 0; i < board.length; i++) {
			for (int j = 0; j < board[i].length; j++) {
				if (board[i][j]==EMPTY_SYMBOL) {
					return false;
				}
			}
		}
		return true;
	}
	/*
	 * Sends the fields diagonal to the field marked by the player to a single array for easier check.
	 */
	private boolean checkDiagonal() {
		char[] array = new char[(numberOfFieldsToWin * 2) - 1];
		int num = numberOfFieldsToWin - 1;
		for (int i = 0; i < array.length; i++) {
			if ((currentRow - num) >= 0 && (currentCol - num) >= 0
					&& (currentRow - num) < this.rows
					&& (currentCol - num) < this.columns) {
				array[i] = board[currentRow - num][currentCol - num];
			}
			num--;
		}
		return checkArray(array);
	}
	/*
	 * Sends the fields anti-diagonal to the field marked by the player to a single array.
	 */
	private boolean checkAntiDiagonal() {
		char[] array = new char[(numberOfFieldsToWin * 2) - 1];
		int num = numberOfFieldsToWin - 1;
		for (int i = 0; i < array.length; i++) {
			if ((currentRow - num) >= 0 && (currentCol + num) >= 0
					&& (currentRow - num) < this.rows
					&& (currentCol + num) < this.columns) {
				array[i] = board[currentRow - num][currentCol + num];
			}
			num--;
		}
		return checkArray(array);
	}
	/*
	 * Sends the fields horizontal to the field marked by the player to a single array.
	 */
	private boolean checkHorizontal() {
		char[] array = new char[(numberOfFieldsToWin * 2) - 1];
		int num = numberOfFieldsToWin - 1;
		for (int i = 0; i < array.length; i++) {
			if (currentRow >= 0 && (currentCol - num) >= 0
					&& currentRow < this.rows
					&& (currentCol - num) < this.columns) {
				array[i] = board[currentRow][currentCol - num];
			}
			num--;

		}
		return checkArray(array);
	}
	/*
	 * Sends the fields vertical to the field marked by the player to a single array.
	 */
	private boolean checkVertical() {
		char[] array = new char[(numberOfFieldsToWin * 2) - 1];
		int num = numberOfFieldsToWin - 1;
		for (int i = 0; i < array.length; i++) {
			if ((currentRow - num) >= 0 && currentCol >= 0
					&& (currentRow - num) < this.rows
					&& currentCol < this.columns) {
				array[i] = board[currentRow - num][currentCol];
			}
			num--;
		}
		return checkArray(array);
	}

	/*
	 * Check if number of repeatable symbols in array(sent like argument from checkVertical(),checkHorizontal(),
	 * checkAntiDiagonal(),checkDiagonal()) is equal to numberOfFieldsToWin set by the player.  
	 */
	private boolean checkArray(char[] array) {
		int counter = 1;
		for (int i = 0; i < array.length - 1; i++) {

			if (array[i] == array[i + 1])
				counter++;
			else
				counter = 1;
			if (counter == numberOfFieldsToWin)
				return true;
		}
		return false;
	}

	/*
	 * Validates and sets value to this.rows.
	 */
	public void setRows(int rows) {
		if (rows < DEFAULT_MIN_NUMBER_OF_ROWS) {
			this.rows = DEFAULT_MIN_NUMBER_OF_ROWS;
			System.out.println("The number of rows will be set to default minimum " + DEFAULT_MIN_NUMBER_OF_ROWS);
		}
		else if (rows > DEFAULT_MAX_NUMBER_OF_ROWS) {
			this.rows = DEFAULT_MAX_NUMBER_OF_ROWS;
			System.out.println("The number of rows will be set to default maximum " + DEFAULT_MAX_NUMBER_OF_ROWS);
		} else {
			this.rows = rows;
		}
	}

	/*
	 * Validates and sets value to this.columns.
	 */
	public void setColumns(int columns) {
		if (columns < DEFAULT_MIN_NUMBER_OF_ROWS) {
			this.columns = DEFAULT_MIN_NUMBER_OF_COLUMNS;
			System.out.println("The number of columns will be set to default minimum " + DEFAULT_MIN_NUMBER_OF_COLUMNS);
		} else if (columns > DEFAULT_MAX_NUMBER_OF_COLUMNS) {
			this.columns = DEFAULT_MAX_NUMBER_OF_COLUMNS;
			System.out.println("The number of columns will be set to default maximum " + DEFAULT_MAX_NUMBER_OF_COLUMNS);

		} else {
			this.columns = columns;
		}
	}

	public void setNumberOfFieldsToWin(int numberOfFieldsToWin) {
		this.numberOfFieldsToWin=Math.min(columns, rows);
		if (this.numberOfFieldsToWin>5) {
			this.numberOfFieldsToWin=DEFAULT_MAX_NUMBER_OF_SIMILAR_FIELDS_TO_WIN;
		}
		System.out.println("The number of similar fields to win will be set to " + this.numberOfFieldsToWin);
	}

	public int getPlayer() {
		if (isFirstPlayer)
			return 1;
		else
			return 2;
	}
}
